home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 476-500 / disk_500 / wiconify / wkeys.lzh / Source / BindWKeys.c next >
C/C++ Source or Header  |  1991-04-21  |  24KB  |  867 lines

  1. /*
  2.  *  BindWKeys.c     Reads a file and creates an array of key bindings
  3.  *                  to be used by a hot-key handler to tell which keys
  4.  *                  do what.
  5.  *
  6.  *             Copyright (c) 1987,1988 by Davide P. Cervone
  7.  *  You may use this code provided this copyright notice is left intact.
  8.  */
  9.  
  10. #include <exec/types.h>
  11. #include <exec/memory.h>
  12. #include <devices/inputevent.h>
  13. #ifndef NO_FILE
  14. #include <stdio.h>
  15. #endif
  16.  
  17. #include "wKeys.h"
  18.  
  19. #ifndef NO_FILE
  20. #define KEYMASK         0x00FF      /* default qualifier key mask */
  21. #define SHIFT           IECODE_UP_PREFIX
  22.  
  23. #define BADVALUE        -1          /* error while parsing input line */
  24.  
  25. /*
  26.  *  Macros to tell whether a character is printable or not, and
  27.  *  whether it is not alphanumeric
  28.  */
  29. #define PRINTABLE(c)    ((c)>=' '&&(c)<='~')
  30. #define NOTPRINTABLE(c) ((c)<' '||(c)>'~')
  31. #define NOTALPHANUM(c)\
  32.    ((c)<'0'||((c)>'9'&&(c)<'A')||((c)>'Z'&&(c)<'a')||(c)>'z')
  33.  
  34. /*
  35.  *  Create a new instance of a given structure type
  36.  */
  37. #define NEW(s,var)      (var = (struct s *)New("var",sizeof(struct s)))
  38.  
  39.  
  40. #define LINESIZE    132
  41. static char InputLine[LINESIZE+1];    /* the line read from the file */
  42. static char *CurPos;                  /* current character position in line */
  43. static char *CurWord;                 /* pointer to begining of current word */
  44. static char TerminationChar;          /* character that ended the word */
  45. static int LineCount = 0;             /* number of lines read from the file */
  46. #endif
  47.  
  48. struct HotKeyItem *KeyList = NULL;    /* the list of key definitions so far */
  49. struct HotKey *KeyArray = NULL;       /* the sorted array of key definitions */
  50. int KeyCount = 0;                     /* the number of key definitions */
  51. #define KEYARRAYSIZE    (KeyCount*sizeof(struct HotKey))
  52.  
  53. #ifndef NO_FILE
  54. static FILE *InFile = NULL;           /* the input file */
  55. #define ERROR _OSERR                  /* the system's error variable */
  56. extern int ERROR;
  57.  
  58.  
  59. /*
  60.  *  This structure maps a character string to a numeric value.  It is used
  61.  *  for mapping key names to keyboard scan codes and key action names to 
  62.  *  action numbers.
  63.  */
  64.  
  65. struct Definition
  66. {
  67.    char *Name;
  68.    UBYTE Code;
  69. };
  70.  
  71.  
  72. /*
  73.  *  Qualifier[] maps the names of the qualifier keys to their corresponding 
  74.  *  bit positions within the ie_Qualifier field of an InputEvent.  This List 
  75.  *  is sorted by name.  Note that LCOMMAND is equivalent to LAMIGA, etc.  
  76.  *  Note also that there are some compound qualifier names, such as SHIFT.  
  77.  *  These are expanded into more than one key definition.
  78.  *
  79.  *  See devices/inputevent.h for more details on qualifier values.
  80.  */
  81.  
  82. static struct Definition Qualifier[] =
  83. {
  84.    {"ALT",20},
  85.    {"AMIGA",22},
  86.    {"CAPSLOCK",2},
  87.    {"CONTROL",3},
  88.    {"INTERRUPT",10},
  89.    {"KEYUP",31},
  90.    {"LALT",4},
  91.    {"LAMIGA",6},
  92.    {"LBUTTON",14},
  93.    {"LCOMMAND",6},
  94.    {"LSHIFT",0},
  95.    {"MBUTTON",12},
  96.    {"MULTIBROADCAST",11},
  97.    {"NUMERICPAD",8},
  98.    {"RALT",5},
  99.    {"RAMIGA",7},
  100.    {"RBUTTON",13},
  101.    {"RCOMMAND",7},
  102.    {"RELATIVEMOUSE",15},
  103.    {"REPEAT",9},
  104.    {"RSHIFT",1},
  105.    {"SHIFT",16},
  106. };
  107. #define MAXQUALIFIER    (sizeof(Qualifier)/sizeof(struct Definition))
  108.  
  109.  
  110. /*
  111.  *  AsciiToKeyCode[] maps ASCII characters to keyboard scan-codes.  This array 
  112.  *  is in ASCII order.  SHIFT indicates that one of the shift keys must be 
  113.  *  held down together with the correct key in order to produce that ASCII 
  114.  *  character.  Note, however, that upper- and lower-case letters both are 
  115.  *  mapped to un-shifted keys.
  116.  */
  117.  
  118. static UBYTE AsciiToKeyCode[] =
  119. {
  120.    0x01 | SHIFT,    /* ! */
  121.    0x2A | SHIFT,    /* " */
  122.    0x03 | SHIFT,    /* # */
  123.    0x04 | SHIFT,    /* $ */
  124.    0x05 | SHIFT,    /* % */
  125.    0x07 | SHIFT,    /* & */
  126.    0x2A,            /* ' */
  127.    0x09 | SHIFT,    /* ( */
  128.    0x0A | SHIFT,    /* ) */
  129.    0x08 | SHIFT,    /* * */
  130.    0x0C | SHIFT,    /* + */
  131.    0x38,            /* , */
  132.    0x0B,            /* - */
  133.    0x39,            /* . */
  134.    0x3A,            /* / */
  135.    0x0A,            /* 0 */
  136.    0x01,            /* 1 */
  137.    0x02,            /* 2 */
  138.    0x03,            /* 3 */
  139.    0x04,            /* 4 */
  140.    0x05,            /* 5 */
  141.    0x06,            /* 6 */
  142.    0x07,            /* 7 */
  143.    0x08,            /* 8 */
  144.    0x09,            /* 9 */
  145.    0x29 | SHIFT,    /* : */
  146.    0x29,            /* ; */
  147.    0x38 | SHIFT,    /* < */
  148.    0x0C,            /* = */
  149.    0x39 | SHIFT,    /* > */
  150.    0x3A | SHIFT,    /* ? */
  151.    0x02 | SHIFT,    /* @ */
  152.    0x20,            /* A */
  153.    0x35,            /* B */
  154.    0x33,            /* C */
  155.    0x22,            /* D */
  156.    0x12,            /* E */
  157.    0x23,            /* F */
  158.    0x24,            /* G */
  159.    0x25,            /* H */
  160.    0x17,            /* I */
  161.    0x26,            /* J */
  162.    0x27,            /* K */
  163.    0x28,            /* L */
  164.    0x37,            /* M */
  165.    0x36,            /* N */
  166.    0x18,            /* O */
  167.    0x19,            /* P */
  168.    0x10,            /* Q */
  169.    0x13,            /* R */
  170.    0x21,            /* S */
  171.    0x14,            /* T */
  172.    0x16,            /* U */
  173.    0x34,            /* V */
  174.    0x11,            /* W */
  175.    0x32,            /* X */
  176.    0x15,            /* Y */
  177.    0x31,            /* Z */
  178.    0x1A,            /* [ */
  179.    0x0D | SHIFT,    /* \ */
  180.    0x1B,            /* ] */
  181.    0x06 | SHIFT,    /* ^ */
  182.    0x0B | SHIFT,    /* _ */
  183.    0x00,            /* ` */
  184.    0x20,            /* a */
  185.    0x35,            /* b */
  186.    0x33,            /* c */
  187.    0x22,            /* d */
  188.    0x12,            /* e */
  189.    0x23,            /* f */
  190.    0x24,            /* g */
  191.    0x25,            /* h */
  192.    0x17,            /* i */
  193.    0x26,            /* j */
  194.    0x27,            /* k */
  195.    0x28,            /* l */
  196.    0x37,            /* m */
  197.    0x36,            /* n */
  198.    0x18,            /* o */
  199.    0x19,            /* p */
  200.    0x10,            /* q */
  201.    0x13,            /* r */
  202.    0x21,            /* s */
  203.    0x14,            /* t */
  204.    0x16,            /* u */
  205.    0x34,            /* v */
  206.    0x11,            /* w */
  207.    0x32,            /* x */
  208.    0x15,            /* y */
  209.    0x31,            /* z */
  210.    0x1A | SHIFT,    /* { */
  211.    0x0D | SHIFT,    /* | */
  212.    0x1B | SHIFT,    /* } */
  213.    0x00 | SHIFT,    /* ~ */
  214. };
  215. #define MAXASCII    sizeof(AsciiToKeyCode)
  216.  
  217.  
  218. /*
  219.  *  Key[] maps key names to their keyboard scan-codes.  This array is sorted
  220.  *  by name.  SHIFT means that a SHIFT key must be held down in addition to
  221.  *  the indicated key.  Some keys have more than one name (e.g., ESCAPE is
  222.  *  equivalent to ESC).  Note that pressing a qualifier key also can be
  223.  *  detected.
  224.  */
  225.  
  226. static struct Definition Key[] =
  227. {
  228.    {"BACKSPACE",0x41},
  229.    {"BS", 0x41},
  230.    {"CAPSLOCKKEY",0x62},
  231.    {"COLON",0x29 | SHIFT},
  232.    {"COMMA",0x38},
  233.    {"CONTROLKEY",0x63},
  234.    {"DASH",0x0B},
  235.    {"DEL",0x46},
  236.    {"DELETE",0x46},
  237.    {"DOT",0x3C},
  238.    {"DOWNARROW",0x4D},
  239.    {"ENTER",0x42},
  240.    {"ESC",0x45},
  241.    {"ESCAPE",0x45},
  242.    {"F1",0x50},
  243.    {"F10",0x59},
  244.    {"F2",0x51},
  245.    {"F3",0x52},
  246.    {"F4",0x53},
  247.    {"F5",0x54},
  248.    {"F6",0x55},
  249.    {"F7",0x56},
  250.    {"F8",0x57},
  251.    {"F9",0x58},
  252.    {"HELP",0x5F},
  253.    {"KP0",0x0F},
  254.    {"KP1",0x1D},
  255.    {"KP2",0x1E},
  256.    {"KP3",0x1F},
  257.    {"KP4",0x2D},
  258.    {"KP5",0x2E},
  259.    {"KP6",0x2F},
  260.    {"KP7",0x3D},
  261.    {"KP8",0x3E},
  262.    {"KP9",0x3F},
  263.    {"LALTKEY",0x64},
  264.    {"LAMIGAKEY",0x66},
  265.    {"LCOMMANDKEY",0x66},
  266.    {"LEFTARROW",0x4F},
  267.    {"LSHIFTKEY",0x60},
  268.    {"MINUS",0x4A},
  269.    {"RALTKEY",0x65},
  270.    {"RAMIGAKEY",0x67},
  271.    {"RCOMMANDKEY",0x67},
  272.    {"RETURN",0x44},
  273.    {"RIGHTARROW",0x4E},
  274.    {"RSHIFTKEY",0x61},
  275.    {"SPACE",0x40},
  276.    {"TAB",0x42},
  277.    {"UPARROW",0x4C},
  278. };
  279. #define MAXKEY      (sizeof(Key)/sizeof(struct Definition)) 
  280.  
  281.  
  282. /*
  283.  *  Action[] maps key action names to their action numbers (used by the
  284.  *  input handler to perform the action when the key is pressed).  This array
  285.  *  is sorted by name.
  286.  */
  287.  
  288. static struct Definition Action[] =
  289. {
  290.    {"BACK-WINDOW-TO-FRONT", BACKTOFRONT},
  291.    {"CLOSE-WINDOW",         CLOSETHEWINDOW},
  292.    {"FRONT-WINDOW-TO-BACK", FRONTTOBACK},
  293.    {"ICON-TO-WINDOW",       ICONTOWINDOW},
  294.    {"NEXT-WINDOW",          ACTIVATENEXT},
  295.    {"PREVIOUS-WINDOW",      ACTIVATEPREVIOUS},
  296.    {"SCREEN-TO-BACK",       SCREENTOBACK},
  297.    {"SCREEN-TO-FRONT",      SCREENTOFRONT},
  298.    {"SELECT-NEXT-ICON",     SELECTNEXTICON},
  299.    {"WINDOW-TO-BACK",       WINDOWTOBACK},
  300.    {"WINDOW-TO-FRONT",      WINDOWTOFRONT},
  301.    {"WINDOW-TO-ICON",       WINDOWTOICON},
  302. };
  303. #define MAXACTION   (sizeof(Action)/sizeof(struct Definition))
  304. #endif
  305.  
  306. /*
  307.  *  Some shorthand macros used to define the default key layout
  308.  */
  309.  
  310. #define RAMIGA      IEQUALIFIER_RCOMMAND
  311. #define RSHIFT      IEQUALIFIER_RSHIFT
  312.  
  313. #define UPARROW     0x4C
  314. #define DOWNARROW   0x4D
  315. #define RIGHTARROW  0x4E
  316. #define LEFTARROW   0x4F
  317. #define BSKEY       0x41
  318. #define TABKEY      0x42
  319. #define RETURNKEY   0x44
  320. #define DELETEKEY   0x46
  321.  
  322. #define HOTKEY(q,c,a)  {{c,0,q},{0xFF,a,RAMIGA|RSHIFT}}
  323.  
  324.  
  325. /*
  326.  *  DefaultKey[] maps the default key layout.  This array is sorted by
  327.  *  KeyCode value.  Change this array to change the default key bindings.
  328.  */
  329.  
  330. static struct HotKey DefaultKey[] =
  331. {
  332.    HOTKEY(          RAMIGA, BSKEY,      ICONTOWINDOW),
  333.    HOTKEY(          RAMIGA, TABKEY,     SELECTNEXTICON),
  334.    HOTKEY(          RAMIGA, RETURNKEY,  WINDOWTOICON),
  335.    HOTKEY(          RAMIGA, DELETEKEY,  CLOSETHEWINDOW),
  336.    HOTKEY(          RAMIGA, UPARROW,    WINDOWTOFRONT),
  337.    HOTKEY( RSHIFT | RAMIGA, UPARROW,    SCREENTOFRONT),
  338.    HOTKEY(          RAMIGA, DOWNARROW,  WINDOWTOBACK),
  339.    HOTKEY( RSHIFT | RAMIGA, DOWNARROW,  SCREENTOBACK),
  340.    HOTKEY(          RAMIGA, RIGHTARROW, ACTIVATENEXT),
  341.    HOTKEY( RSHIFT | RAMIGA, RIGHTARROW, FRONTTOBACK),
  342.    HOTKEY(          RAMIGA, LEFTARROW,  ACTIVATEPREVIOUS),
  343.    HOTKEY( RSHIFT | RAMIGA, LEFTARROW,  BACKTOFRONT),
  344. };
  345. #define DEFAULTSIZE (sizeof(DefaultKey)/sizeof(struct HotKey))
  346.  
  347.  
  348. #ifndef NO_FILE
  349. /*
  350.  *  Error()
  351.  *
  352.  *  Print an error message and the line number where the error occured.
  353.  *  Return the error value.
  354.  */
  355.  
  356. static int Error(s,x1,x2,x3)
  357. char *s, *x1,*x2,*x3;
  358. {
  359.    printf("Line %2d:  ",LineCount);
  360.    printf(s,x1,x2,x3);
  361.    printf("\n");
  362.    return(BADVALUE);
  363. }
  364.  
  365.  
  366. /*
  367.  *  GetNextWord()
  368.  *
  369.  *  Isolate the next word in the line read from the file.
  370.  *  If we are not at the end of the line, then
  371.  *    skip over leading spaces,
  372.  *    set CurWord to point to the beginning of the word,
  373.  *    while we are not at the end of the word,
  374.  *      check if the current character is a word delimiter:
  375.  *      if it is a space or a tab,
  376.  *        skip additional spaces or tabs,
  377.  *        set the terminator character,
  378.  *        and end the word.  
  379.  *        (At this point, CurPos will be pointing to the "real" delimiter,
  380.  *        or to the beginning of the next word, if the delimiter really was
  381.  *        a space).
  382.  *      if it was a NULL, convert it to a new-line.
  383.  *      if it was a dash, comma, colon or new-line,
  384.  *        save the termination character for later,
  385.  *        replace the charactger with a NULL so that CurWord will end at
  386.  *          the end of the word,
  387.  *        and stop looking for more of the word.
  388.  *      otherwise
  389.  *       if we're still looking for the end of the word (i.e., we have not
  390.  *         ended, it by hitting a space),
  391.  *         if its unprintable or the current word is more than one character
  392.  *           long and the current character is not alphanumeric,
  393.  *           then record the bad character and end the word
  394.  *         go on to the next character (i.e., add the current one to the word)
  395.  */
  396.  
  397. static void GetNextWord()
  398. {
  399.    short NotDone = TRUE;
  400.    char c;
  401.  
  402.    if (TerminationChar != '\n')
  403.    {
  404.       while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
  405.       CurWord = CurPos;
  406.       while (NotDone)
  407.       {
  408.          if (*CurPos == ' ' || *CurPos == '\t')
  409.          {
  410.             *CurPos = '\0';
  411.             while (*(++CurPos) == ' ' || *CurPos == '\t');
  412.             TerminationChar = ' ';
  413.             NotDone = FALSE;
  414.          }
  415.          switch(c = *CurPos)
  416.          {
  417.             case '\0':
  418.                c = *CurPos = '\n';
  419.             case '-':
  420.             case ',':
  421.             case ':':
  422.             case '\n':
  423.                TerminationChar = c;
  424.                *CurPos++ = '\0';
  425.                NotDone = FALSE;
  426.                break;
  427.  
  428.             default:
  429.                if (NotDone)
  430.                {
  431.                   if (NOTPRINTABLE(c) || (CurPos != CurWord && NOTALPHANUM(c)))
  432.                   {
  433.                      TerminationChar = c;
  434.                      NotDone = FALSE;
  435.                   }
  436.                   CurPos++;
  437.                }
  438.                break;
  439.          }
  440.       }
  441.    }
  442. }
  443.  
  444.  
  445. /*
  446.  *  FindWord()
  447.  *
  448.  *  Search the KeyWord array (containing Count entries) for an entry with
  449.  *  Name equal to theWord.  Return the corresponding Code value, or BADVALUE
  450.  *  if theWord does not appear in the KeyWord array.
  451.  *
  452.  *  KeyWord[] must be sorted by name, since we use a binary search to find
  453.  *  theWord witin it.
  454.  */
  455.  
  456. static int FindWord(theWord,KeyWord,Count)
  457. char *theWord;
  458. struct Definition KeyWord[];
  459. int Count;
  460. {
  461.    int value = BADVALUE;
  462.    register short Min,Max, Num;
  463.    register int comp;
  464.    
  465.    Max = Count; Min = -1;
  466.    while ((Num = (Min + Max) >> 1) != Min && value == BADVALUE)
  467.    {
  468.       comp = stricmp(theWord,KeyWord[Num].Name);
  469.       if (comp < 0) Max = Num; else if (comp > 0) Min = Num;
  470.          else value = KeyWord[Num].Code;
  471.    }
  472.    return(value);
  473. }
  474.  
  475.  
  476. /*
  477.  *  FindQualifier()
  478.  *
  479.  *  Find a qualifier name in the Qualifier[] array.
  480.  */
  481. #define FindQualifier(w)    FindWord(w,Qualifier,MAXQUALIFIER)
  482.  
  483.  
  484. /*
  485.  *  FindKeyCode()
  486.  *
  487.  *  Find the scan-code for the given key name.  If the key name is a single
  488.  *  ASCII character, use the AsciiToKeyCode array to look up the scan-code
  489.  *  directly, otherwise use FindWord() to search the Key[] array.
  490.  */
  491.  
  492. static int FindKeyCode(theWord)
  493. char *theWord;
  494. {
  495.    int value = BADVALUE;
  496.    
  497.    if (strlen(theWord) == 1)
  498.    {
  499.       if (PRINTABLE(*theWord)) value = AsciiToKeyCode[*theWord-'!'];
  500.    } else {
  501.       value = FindWord(theWord,Key,MAXKEY);
  502.    }
  503.    return(value);
  504. }
  505.  
  506.  
  507. /*
  508.  *  GetKeyCode()
  509.  *
  510.  *  Parse a set of qualifiers and a key name and return the KeyCode longword
  511.  *  that describes the designated key.  SHIFT, AMIGA, and ALT flags are 
  512.  *  used to indicate when more than one key is designated.  Return BADVALUE
  513.  *  if there is an error parsing the line.
  514.  *
  515.  *  Get the next word on the line, and check the termination character.  
  516.  *  Qualifiers end with dashes, commas, or spaces; key-names end with a colon.
  517.  *  Try to find the qualifier or key-name in the proper list, and give an error
  518.  *  if it can not be found, or if the name is null.  For a qualifier, set its
  519.  *  flag bit in the KeyCode longword.  For key-names, set the scan-code and
  520.  *  the set the SHIFT bit in the qualifier flag bits if necessary.
  521.  *  If the end of the line is reached, display an error.  If some other
  522.  *  delimiter was found, then display an error (making unprintable characters
  523.  *  printable).
  524.  *
  525.  *  Once a key-name is found, stop looking for more words.
  526.  */
  527.  
  528. static void GetKeyCode(theKey)
  529. long *theKey;
  530. {
  531.    short NotDone = TRUE;
  532.    int value;
  533.  
  534.    *theKey = 0;
  535.    while (NotDone)
  536.    {
  537.       GetNextWord();
  538.       switch(TerminationChar)
  539.       {
  540.          case '-':
  541.          case ',':
  542.          case ' ':
  543.             if (strlen(CurWord))
  544.             {
  545.                value = FindQualifier(CurWord);
  546.                if (value > BADVALUE)
  547.                   *theKey |= (1 << value);
  548.                  else
  549.                   *theKey = Error("Unrecognized key qualifier '%s'",CurWord);
  550.             } else {
  551.                *theKey = Error("Missing qualifier keyword");
  552.             }
  553.             break;
  554.  
  555.          case ':':
  556.             if (strlen(CurWord))
  557.             {
  558.                value = FindKeyCode(CurWord);
  559.                if (value > BADVALUE)
  560.                {
  561.                   *theKey |= ((value & (~SHIFT)) << 24) |
  562.                              ((value & SHIFT) << 9);
  563.                } else {
  564.                   *theKey = Error("Unrecognized key name '%s'",CurWord);
  565.                }
  566.             } else {
  567.                *theKey = Error("Key name not specified");
  568.             }
  569.             NotDone = FALSE;
  570.             break;
  571.  
  572.          case '\n':
  573.             *theKey = Error("Key name ends prematurely");
  574.             NotDone = FALSE;
  575.             break;
  576.  
  577.          default:
  578.             if ((TerminationChar & 0x7F) >= ' ')
  579.                *theKey = Error("Illegal delimiter character '%c'",
  580.                   (char)TerminationChar);
  581.               else
  582.                *theKey = Error("Illegal delimiter character '^%c'",
  583.                   (char)((TerminationChar & 0x7F) + '@'));
  584.             break;
  585.       }
  586.    }
  587. }
  588.  
  589.  
  590. /*
  591.  *  GetKeyAction()
  592.  *
  593.  *  Parse the rest of the input line for a key-action keyword.  First, remove
  594.  *  leading and training blanks, and replace the final new-line with a NULL.
  595.  *  Look up the remainder of the line (if any) in the Action[] array, and
  596.  *  if it is not found, report the error.
  597.  */
  598.  
  599. static void GetKeyAction(theAction)
  600. short *theAction;
  601. {
  602.    *theAction = BADVALUE;
  603.  
  604.    while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
  605.    CurWord = CurPos;
  606.    CurPos = CurPos + strlen(CurWord) - 1;
  607.    if (*CurPos == '\n') *CurPos-- = '\0';
  608.    while (*CurPos == ' ' || *CurPos == '\t') *CurPos-- = '\0';
  609.  
  610.    if (*CurWord)
  611.    {
  612.       *theAction = FindWord(CurWord,Action,MAXACTION);
  613.       if (*theAction == BADVALUE) Error("Unrecognized action '%s'",CurWord);
  614.    } else {
  615.       Error("No action specified");
  616.    }
  617. }
  618.  
  619.  
  620. /*
  621.  *  KeyCompare()
  622.  *
  623.  *  Return a positive number if the first key is bigger than the second,
  624.  *  zero if they are equal, and a negative number if the second key is 
  625.  *  bigger.  This routine is used by mSort() to sort the keys.
  626.  */
  627.  
  628. static int KeyCompare(key1,key2)
  629. struct HotKeyItem *key1, *key2;
  630. {
  631.    return(key1->hki_KeyCode - key2->hki_KeyCode);
  632. }
  633.  
  634.  
  635. /*
  636.  *
  637.  *  KeyDispose()
  638.  *
  639.  *  Remove a duplicate key definition and report the fact that a key is
  640.  *  multiply defined (it takes a little work to get the qualifier and key
  641.  *  names back out of the arrays).  This routine is called by mSort() when
  642.  *  it finds duplicate keys.
  643.  */
  644.  
  645. static void KeyDispose(key)
  646. struct HotKeyItem *key;
  647. {
  648.    short i;
  649.    UBYTE code = key->hki_Code & 0x7F;
  650.    int KeyNotFound = TRUE;
  651.    long mask = 0xFFFFFFFF;
  652.  
  653.    printf("Key ");
  654.    for (i=0; i<MAXQUALIFIER; i++)
  655.       if (key->hki_KeyCode & (1 << Qualifier[i].Code) & mask)
  656.       {
  657.          printf("%s-",Qualifier[i].Name);
  658.          mask &= ~(1 << Qualifier[i].Code);
  659.       }
  660.    for (i=0; i<MAXKEY && KeyNotFound; i++)
  661.       if (code == Key[i].Code)
  662.       {
  663.          printf("%s",Key[i].Name);
  664.          KeyNotFound = FALSE;
  665.       }
  666.    for (i=0; i<MAXASCII && KeyNotFound; i++)
  667.    {
  668.       if (code == AsciiToKeyCode[i])
  669.       {
  670.          printf("%c",i+'!');
  671.          KeyNotFound = FALSE;
  672.       }
  673.    }
  674.    printf(" multiply defined\n");
  675.    KeyCount--;
  676.    key->Next = key->Prev = NULL;
  677.    FreeMem(key,sizeof(*key));
  678. }
  679.  
  680.  
  681. /*
  682.  *  GetKeyList()
  683.  *
  684.  *  Read each line from the file (incrementing the line count as we go), 
  685.  *  and skip leading spaces and blank lines.  Get the key code and key action
  686.  *  specified on the line.  If no error was found, add the key definition 
  687.  *  to the linked list of keys defined.  If SHIFT, AMIGA, or ALT were specified,
  688.  *  then add one key each for the left and right version of that qualifier.
  689.  */
  690.  
  691. static void GetKeyList()
  692. {
  693.    long theKey;
  694.    short theAction;
  695.    struct HotKeyItem *TempKey;
  696.    UWORD mask,multikeys;
  697.    extern char *fgets();
  698.  
  699.    while (feof(InFile) == FALSE)
  700.    {
  701.       CurPos = fgets(InputLine,LINESIZE,InFile);
  702.       TerminationChar = '\0';
  703.       LineCount++;
  704.       while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
  705.  
  706.       if (CurPos != NULL && *CurPos != '\0' && *CurPos != '\n')
  707.       {
  708.          GetKeyCode(&theKey);
  709.          GetKeyAction(&theAction);
  710.          if (theKey != BADVALUE && theAction != BADVALUE)
  711.          {
  712.             multikeys = (theKey >> 15) & 0xFE;
  713.             multikeys |= multikeys << 1;
  714.             if (multikeys == 0) multikeys = 1;
  715.             for (mask=1; multikeys; mask<<=1,multikeys>>=1)
  716.             {
  717.                if (multikeys & 1)
  718.                {
  719.                   NEW(HotKeyItem,TempKey);
  720.                   TempKey->hki_KeyCode = theKey;
  721.                   TempKey->hki_Flags   = 0;
  722.                   TempKey->hki_Qual   |= mask >> 1;
  723.                   TempKey->hki_KeyMask = 0xFFFFFFFF;
  724.                   TempKey->hki_Action  = theAction;
  725.                   TempKey->Next = KeyList;
  726.                   KeyList = TempKey;
  727.                   KeyCount++;
  728.                }
  729.             }
  730.          }
  731.       }
  732.    }
  733. }
  734.  
  735.  
  736. /*
  737.  *  SetKeyMasks()
  738.  *
  739.  *  For each key scan-code, we OR together the qualifier masks for all the
  740.  *  definitions for that scan-code and set the key Mask value for each
  741.  *  key with that scan-code to the final ORed mask.  That is, the Mask value
  742.  *  indicates what qualifiers are important for determining when a key has
  743.  *  been pressed (and distinguishing it from other definitions using the
  744.  *  same scan-code but different qualifiers).
  745.  */
  746.  
  747. static void SetKeyMasks()
  748. {
  749.    struct HotKeyItem *CurKey = KeyList;
  750.    struct HotKeyItem *LastKey = CurKey;
  751.    UWORD Mask;
  752.  
  753.    while (LastKey)
  754.    {
  755.       Mask = 0;
  756.       while (CurKey && CurKey->hki_Code == LastKey->hki_Code)
  757.       {
  758.          Mask |= CurKey->hki_Qual;
  759.          CurKey = CurKey->Next;
  760.       }
  761.       if (Mask == 0) Mask = KEYMASK;
  762.       do
  763.       {
  764.          LastKey->hki_Mask = Mask;
  765.          LastKey = LastKey->Next;
  766.       } while (LastKey != CurKey);
  767.    }
  768. }
  769.  
  770.  
  771. /*
  772.  *  MakeKeyArray()
  773.  *
  774.  *  If there are any keys defined, allocate enough space for the KeyArray
  775.  *  that will contain the key definitions, then go through the list and
  776.  *  copy the definitions into the array.  Free each item from the list once it
  777.  *  is copied.  The array saves space (it does not need to contain pointers 
  778.  *  to next and previous items), and allows for easy implementation of a 
  779.  *  binary search on the array.
  780.  */
  781.  
  782. static void MakeKeyArray()
  783. {
  784.    struct HotKeyItem *TempKey;
  785.    short i;
  786.  
  787.    if (KeyCount)
  788.    {
  789.       KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE);
  790.       for (i=0; i<KeyCount; i++)
  791.       {
  792.          KeyArray[i].hk_KeyCode = KeyList->hki_KeyCode;
  793.          KeyArray[i].hk_KeyMask = KeyList->hki_KeyMask;
  794.          TempKey = KeyList; KeyList = KeyList->Next;
  795.          FreeMem(TempKey,sizeof(*TempKey));
  796.       }
  797.    }
  798. }
  799. #endif
  800.  
  801.  
  802. /*
  803.  *  MakeDefaultArray()
  804.  *
  805.  *  Copy the DefaultKey[] array into a dynamically allocated array that can
  806.  *  be passed to the input handler and still remain in memory even when the
  807.  *  original process is unloaded.
  808.  */
  809.  
  810. static void MakeDefaultArray()
  811. {
  812.    short i;
  813.    
  814.    KeyCount = DEFAULTSIZE;
  815.    KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE);
  816.    for (i=0; i<KeyCount; i++)
  817.    {
  818.       KeyArray[i].hk_KeyCode = DefaultKey[i].hk_KeyCode;
  819.       KeyArray[i].hk_KeyMask = DefaultKey[i].hk_KeyMask;
  820.    }
  821. }
  822.  
  823.  
  824. /*
  825.  *  GetKeyArray()
  826.  *
  827.  *  If there is a command-line argument, then 
  828.  *    it must be a file name, so try to open it (error if there is a problem).
  829.  *    Create the key definition list from the lines in the file.
  830.  *    If there were no valid key definitions, 
  831.  *      say so,
  832.  *     otherwise, 
  833.  *      sort the key list,
  834.  *      set the key masks for each scan-code,
  835.  *      make the key array from the sorted list.
  836.  *   otherwise (there was no file name given, so)
  837.  *    make the key array from the default key list.
  838.  */
  839.  
  840. void GetKeyArray(argc,argv)
  841. int argc;
  842. char *argv[];
  843. {
  844. #ifndef NO_FILE
  845.    extern struct HotKeyItem *mSort(); 
  846.  
  847.    if (argc > 1)
  848.    {
  849.       InFile = fopen(argv[1],"r");
  850.       if (InFile == NULL)
  851.          DoExit("Can't Open File '%s':  Error %d",argv[1],ERROR);
  852.       GetKeyList();
  853.       if (KeyCount == 0)
  854.       {
  855.          DoExit("No valid key definitions found in file '%s'",argv[1]);
  856.       } else {
  857.          KeyList = mSort(KeyList,KeyCompare,KeyDispose);
  858.          SetKeyMasks();
  859.          MakeKeyArray();
  860.       }
  861.    } else
  862. #endif
  863.    {
  864.       MakeDefaultArray();
  865.    }
  866. }
  867.